/*	Copyright 2013 RangerUFO
 *
 *	This file is part of Libevent-helper.
 *
 *	Libevent-helper is free software: you can redistribute it and/or modify
 *	it under the terms of the GNU Lesser General Public License as published by
 *	the Free Software Foundation, either version 3 of the License, or
 *	(at your option) any later version.
 *
 *	Libevent-helper is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *	GNU Lesser General Public License for more details.
 *
 *	You should have received a copy of the GNU Lesser General Public License
 *	along with Libevent-helper.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "event2/helper.h"
#include <event2/event.h>
#include <event2/thread.h>
#include <stdlib.h>
#include <string.h>

static evhelper_malloc_fn mem_malloc_fn = 0;
static evhelper_realloc_fn mem_realloc_fn = 0;
static evhelper_free_fn mem_free_fn = 0;

int evhelper_init()
{
	return evthread_use_pthreads();
}

void evhelper_set_mem_functions(
		evhelper_malloc_fn malloc_fn,
		evhelper_realloc_fn realloc_fn,
		evhelper_free_fn free_fn)
{
	mem_malloc_fn = malloc_fn;
	mem_realloc_fn = realloc_fn;
	mem_free_fn = free_fn;

	event_set_mem_functions(malloc_fn, realloc_fn, free_fn);
}

void* evhelper_malloc(size_t n)
{
	if (mem_malloc_fn)
	{
		return mem_malloc_fn(n);
	}
	else
	{
		return malloc(n);
	}
}

void* evhelper_realloc(void* p, size_t n)
{
	if (mem_realloc_fn)
	{
		return mem_realloc_fn(p, n);
	}
	else
	{
		return realloc(p, n);
	}
}

void evhelper_free(void* p)
{
	if (mem_free_fn)
	{
		mem_free_fn(p);
	}
	else
	{
		free(p);
	}
}


struct evtable_node;

struct evtable
{
	struct evtable_node* nodes;
	size_t cap;
	evtable_node_destroy_fn destroy_fn;
	struct evtable_node* free_list;
};

struct evtable_node
{
	void* obj;
	struct evtable* table;
	struct evtable_node* next;
};

struct evtable* evtable_new(size_t n, evtable_node_destroy_fn destroy_fn)
{
	struct evtable* tb = (struct evtable*)evhelper_malloc(sizeof(struct evtable));

	if (!tb)
	{
		return 0;
	}

	tb->nodes = (struct evtable_node*)evhelper_malloc(sizeof(struct evtable_node) * n);

	if (!tb->nodes)
	{
		evhelper_free(tb);
		return 0;
	}

	memset(tb->nodes, 0, sizeof(struct evtable_node) * n);

	tb->cap = n;
	tb->destroy_fn = destroy_fn;

	for (size_t i = 0; i < n - 1; ++i)
	{
		tb->nodes[i].next = tb->nodes + i + 1;
	}

	tb->free_list = tb->nodes;

	return tb;
}

void evtable_free(struct evtable* tb)
{
	if (!tb)
	{
		return;
	}

	if (tb->destroy_fn)
	{
		for (struct evtable_node* i = tb->nodes; i < tb->nodes + tb->cap; ++i)
		{
			if (i->obj)
			{
				tb->destroy_fn(i);
			}
		}
	}

	evhelper_free(tb->nodes);
	evhelper_free(tb);
}

struct evtable_node* evtable_insert(struct evtable* tb, void* obj)
{
	if (!tb)
	{
		return 0;
	}

	if (!tb->free_list)
	{
		return 0;
	}

	struct evtable_node* tn = tb->free_list;
	tb->free_list = tn->next;
	tn->obj = obj;
	tn->table = tb;

	return tn;
}

void evtable_remove(struct evtable* tb, struct evtable_node* tn)
{
	if (!tb)
	{
		return;
	}

	if (!tn)
	{
		return;
	}

	if (tn < tb->nodes || tn >= tb->nodes + tb->cap)
	{
		return;
	}

	if (tn->table != tb)
	{
		return;
	}

	if (!tn->obj)
	{
		return;
	}

	if (tb->destroy_fn)
	{
		tb->destroy_fn(tn);
	}

	tn->obj = 0;
	tn->table = 0;
	tn->next = tb->free_list;
	tb->free_list = tn;
}

void* evtable_node_deref(struct evtable_node* tn)
{
	if (!tn)
	{
		return 0;
	}

	return tn->obj;
}

struct evtable* evtable_node_table(struct evtable_node* tn)
{
	if (!tn)
	{
		return 0;
	}

	return tn->table;
}

struct evtable_node* evtable_find(struct evtable* tb, void* obj)
{
	if (!tb)
	{
		return 0;
	}

	for (struct evtable_node* i = tb->nodes; i < tb->nodes + tb->cap; ++i)
	{
		if (i->obj == obj)
		{
			return i;
		}
	}

	return 0;
}

struct evtable_node* evtable_get_first(struct evtable* tb)
{
	if (!tb)
	{
		return 0;
	}

	for (struct evtable_node* i = tb->nodes; i < tb->nodes + tb->cap; ++i)
	{
		if (i->obj)
		{
			return i;
		}
	}

	return 0;
}

struct evtable_node* evtable_get_next(struct evtable* tb, struct evtable_node* tn)
{
	if (!tb)
	{
		return 0;
	}

	if (!tn)
	{
		return 0;
	}

	if (tn < tb->nodes || tn >= tb->nodes + tb->cap)
	{
		return 0;
	}

	if (tn->table != tb)
	{
		return 0;
	}

	for (struct evtable_node* i = tn + 1; i < tb->nodes + tb->cap; ++i)
	{
		if (i->obj)
		{
			return i;
		}
	}

	return 0;
}
